package com.example.common.event

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.lang.NullPointerException

object LiveDataBus {
    private val bus:MutableMap<String,BusMutableLiveData<Any>> = mutableMapOf()

    fun <T> with(key:String):BusMutableLiveData<T>{
        if(!bus.containsKey(key)){
            bus[key] = BusMutableLiveData()
        }
        return bus[key] as BusMutableLiveData<T>
    }



    class BusMutableLiveData<T>:MutableLiveData<T>(){

        //存储所有订阅方
        private val observerMap: MutableMap<Observer<*>, Observer<*>> = mutableMapOf()


        /**
         * 订阅前台事件
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer)
            try {
                hookSticky(observer)
            } catch (e: Exception){
                e.printStackTrace()
            }
        }

        /**
         * 订阅前台粘性事件
         */
        fun observeSticky(owner: LifecycleOwner,  observer: (T) -> Unit){
            super.observe(owner, observer)
        }


        /**
         * 订阅后台事件
         */
        override fun observeForever(observer: Observer<in T>) {
            if (!observerMap.containsKey(observer)){
                observerMap[observer] = ObserverWrapper(observer)
            }
            super.observeForever(observerMap[observer] as Observer<in T>)
        }

        /**
         * 订阅后台粘性事件
         */
        fun observeForeverSticky(observer: Observer<in T>){
            super.observeForever(observer)
        }

        /**
         * 取消订阅事件
         */
        override fun removeObserver(observer: Observer<in T>) {
            val realObserver: Observer<in T> = if (observerMap.containsKey(observer)){
                observerMap.remove(observer) as Observer<in T>
            } else {
                observer
            }
            super.removeObserver(realObserver)
        }

        /**
         * 使用反射技术解决非粘性问题
         */
        private fun hookSticky(observer: Observer<*>){
            //获取LiveData的Class对象
            val liveDataClass = LiveData::class.java
            //获取LiveData中数据版本号：mVersion
            val mVersionField = liveDataClass.getDeclaredField("mVersion")
            mVersionField.isAccessible = true
            val dataVersion = mVersionField[this]

            //获取订阅方集合
            val mObserversFieldMap = liveDataClass.getDeclaredField("mObservers")
            mObserversFieldMap.isAccessible = true
            val observers = mObserversFieldMap[this]

            val observerClass: Class<*> = observers.javaClass
            val methodGet = observerClass.getDeclaredMethod("get", Any::class.java)
            methodGet.isAccessible = true

            //执行Get方法
            val observerObject = methodGet.invoke(observers, observer)
            var subObserverWrapper: Any? = null
            if (observerObject is Map.Entry<*, *>){
                subObserverWrapper = observerObject.value
            }

            //校验Observer
            if (subObserverWrapper == null){
                throw  NullPointerException("Wrapper can not is null !!")
            }

            val observerWrapperClass: Class<*> ? = subObserverWrapper.javaClass.superclass
            val lastVersionField = observerWrapperClass!!.getDeclaredField("mLastVersion")
            lastVersionField.isAccessible = true

            //修改版本号
            lastVersionField[subObserverWrapper] = dataVersion

        }
    }

    class ObserverWrapper<T>(val observer:Observer<T>):Observer<T>{
        override fun onChanged(value: T) {

            if(isCallOnObserverForever()){
                return
            }
            observer.onChanged(value)
        }
        private fun isCallOnObserverForever():Boolean{
            val stack =  Thread.currentThread().stackTrace
            for (item in stack){
                if ("androidx.lifecycle.LiveData" == item.className && "observeForever" == item.methodName){
                    return true
                }
            }
            return false
        }

    }






}